<!DOCTYPE html>
<!--
Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/math/range.html">
<link rel="import" href="/tracing/base/unit.html">
<link rel="import" href="/tracing/metrics/metric_registry.html">
<link rel="import" href="/tracing/metrics/v8/utils.html">
<link rel="import" href="/tracing/value/histogram.html">

<script>
'use strict';

tr.exportTo('tr.metrics.v8', function() {
  function computeSyncInstantiationTimeMetric(histograms, wasmEvents) {
    if (!wasmEvents.hasOwnProperty('wasm.SyncInstantiate')) return;

    const wasmSyncInstantiationTimeCPU = new tr.v.Histogram(
        'v8:wasm:sync_instantiate:cpu_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmSyncInstantiationTimeCPU.description =
      'cpu time spent instantiating a WebAssembly module';
    const wasmSyncInstantiationTimeWall = new tr.v.Histogram(
        'v8:wasm:sync_instantiate:wall_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmSyncInstantiationTimeWall.description =
      'wall time spent instantiating a WebAssembly module';

    for (const e of wasmEvents['wasm.SyncInstantiate']) {
      wasmSyncInstantiationTimeCPU.addSample(e.cpuDuration);
      wasmSyncInstantiationTimeWall.addSample(e.duration);
    }

    histograms.addHistogram(wasmSyncInstantiationTimeCPU);
    histograms.addHistogram(wasmSyncInstantiationTimeWall);
  }

  function computeSyncCompileTimeMetric(histograms, wasmEvents) {
    if (!wasmEvents.hasOwnProperty('wasm.SyncCompile')) return;

    const wasmSyncCompileTimeCPU = new tr.v.Histogram(
        'v8:wasm:sync_compile:cpu_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmSyncCompileTimeCPU.description =
      'cpu time spent compiling a WebAssembly module synchronously';
    const wasmSyncCompileTimeWall = new tr.v.Histogram(
        'v8:wasm:sync_compile:wall_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmSyncCompileTimeWall.description =
      'wall time spent compiling a WebAssembly module synchronously';

    for (const e of wasmEvents['wasm.SyncCompile']) {
      wasmSyncCompileTimeCPU.addSample(e.cpuDuration);
      wasmSyncCompileTimeWall.addSample(e.duration);
    }

    histograms.addHistogram(wasmSyncCompileTimeCPU);
    histograms.addHistogram(wasmSyncCompileTimeWall);
  }

  function computeDeserializeTimeMetric(histograms, wasmEvents) {
    if (!wasmEvents.hasOwnProperty('wasm.Deserialize')) return;

    const wasmDeserializeTimeCPU = new tr.v.Histogram(
        'v8:wasm:deserialize:cpu_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmDeserializeTimeCPU.description =
      'cpu time spent deserializing a WebAssembly module';
    const wasmDeserializeTimeWall = new tr.v.Histogram(
        'v8:wasm:deserialize:wall_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);
    wasmDeserializeTimeWall.description =
      'wall time spent deserializing a WebAssembly module';

    for (const e of wasmEvents['wasm.Deserialize']) {
      wasmDeserializeTimeCPU.addSample(e.cpuDuration);
      wasmDeserializeTimeWall.addSample(e.duration);
    }

    histograms.addHistogram(wasmDeserializeTimeCPU);
    histograms.addHistogram(wasmDeserializeTimeWall);
  }

  function computeStreamingBaselineCompileTimeMetric(histograms, wasmEvents) {
    // With streaming compilation, baseline compilation happens from when the
    // first bytes get received until when baseline compilation finishes. If
    // compilation speed is faster than download speed, then this metric also
    // includes the time compilation is waiting for additional bytes.
    if (!wasmEvents.hasOwnProperty('wasm.StartStreamingCompilation') ||
        !wasmEvents.hasOwnProperty('wasm.BaselineFinished')) {
      return;
    }
    const histogram = new tr.v.Histogram(
        'v8:wasm:streaming_baseline_compilation:wall_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);

    for (const endEvent of wasmEvents['wasm.BaselineFinished']) {
      const compilationEnd = endEvent.end;
      const startEvent = wasmEvents['wasm.StartStreamingCompilation'].find(
          e => e.args.id === endEvent.args.id);
      if (!startEvent) continue;
      const compilationStart = startEvent.start;
      histogram.addSample(compilationEnd - compilationStart);
    }
    histograms.addHistogram(histogram);
  }

  function computeCompilationTierupWallTimeMetric(histograms, wasmEvents) {
    if (!wasmEvents.hasOwnProperty('wasm.BaselineFinished') ||
        !wasmEvents.hasOwnProperty('wasm.TopTierFinished')) {
      return;
    }

    const histogram = new tr.v.Histogram(
        'v8:wasm:compilation_tierup:wall_time',
        tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);

    for (const endEvent of wasmEvents['wasm.TopTierFinished']) {
      const tierupEnd = endEvent.end;
      const startEvent = wasmEvents['wasm.BaselineFinished'].find(
          e => e.args.id === endEvent.args.id);
      if (!startEvent) continue;
      const tierupStart = startEvent.start;
      histogram.addSample(tierupEnd - tierupStart);
    }

    histograms.addHistogram(histogram);
  }

  function collectWasmEvents(model) {
    const wasmEvents = tr.metrics.v8.utils.filterAndOrderEvents(model,
        event => event.title.startsWith('wasm.'),
        event => event.title);

    return wasmEvents;
  }

  function wasmMetric(histograms, model) {
    const wasmEvents = collectWasmEvents(model);

    computeSyncInstantiationTimeMetric(histograms, wasmEvents);
    computeSyncCompileTimeMetric(histograms, wasmEvents);
    computeDeserializeTimeMetric(histograms, wasmEvents);
    computeStreamingBaselineCompileTimeMetric(histograms, wasmEvents);
    computeCompilationTierupWallTimeMetric(histograms, wasmEvents);
  }

  tr.metrics.MetricRegistry.register(wasmMetric);

  return {
    wasmMetric,
  };
});

</script>
